# -*- coding: utf-8 -*-
import socket
from typing import Dict

import gevent
from zinx.ziface.iconnection import IConnection
from zinx.ziface.imsghandler import IMsgHandler
from zinx.ziface.irequest import IRequest
from zinx.ziface.iserver import IServer
from zinx.znet.message import NewMessage
from zinx.znet.request import NewRequest
from zinx.znet.datapack import NewDataPack
from zinx.utils.globalobj import GlobalObject, GlobalGevents
from gevent.queue import Queue


class Connection(IConnection):
    def __init__(self, tcp_server: IServer, conn: socket.socket, connID: int, remote_addr: tuple, msgHandler: IMsgHandler):
        self.TcpServer = tcp_server
        self.Conn: socket.socket = conn  # 当前链接的socket TCP套接字
        self.ConnID: int = connID  # 链接的ID
        # self.HandlerAPI = handlerAPI  # 当前链接绑定的业务处理方法的API
        self.is_closed: bool = False  # 链接状态
        self.Remote_Addr: tuple = remote_addr # 地址
        # self.Router: IRouter = router
        self.msgHandler: IMsgHandler = msgHandler # 消息处理模块
        self.msgQueue: Queue = Queue() # 写队列
        self.property: Dict[str, object] = {}

    def Start(self):
        """
        启动链接 让当前的链接准备开始工作
        :return:
        """
        print("链接开启，ID=", self.ConnID)
        # 开启写业务
        g1 = gevent.spawn(self.StartWriter)
        # 开启读业务
        g2 = gevent.spawn(self.StartReader)
        GlobalGevents.append(g1)
        GlobalGevents.append(g2)
        # 按照用户传递进来的创建连接时需要处理的业务，执行钩子方法
        self.TcpServer.CallOnConnStart(self)

    def StartReader(self):
        """
        处理读业务
        :return:
        """
        print("开启读业务")
        while True:
            try:
                dp = NewDataPack()
                # 读取客户端的Msg head
                head_data = self.Conn.recv(dp.GetHeadLen())
                if len(head_data) == 0:
                    # head_data 长度为0表示客户端已经退出
                    break
                # 拆包，得到msgID 和 dataLen 放在msg中
                msg = dp.Unpack(head_data)
                # 根据 dataLen 读取 data，放在msg.Data中
                data_content = self.Conn.recv(msg.GetDataLen())
                msg.SetData(data_content)
                # 得到当前conn数据的Request请求数据
                req = NewRequest(self, msg)
                # 执行注册的路由方法
                self.RunHandlerForWorker(req)
                g1 = gevent.spawn(self.RunHandler(req))
                GlobalGevents.append(g1)
            except Exception as e:
                print("读取数据异常 ", e)
                break
        print("读业务关闭，ID", self.ConnID)
        self.Stop()

    def StartWriter(self):
        """
        写业务
        :return:
        """
        print("开启写业务")
        while True:
            try:
                if self.is_closed:
                    break
                msg = self.msgQueue.get_nowait()
                self.Conn.send(msg)
            except:
                # 由于msgQueue的get_nowait并不会主动切换其他协程，使用sleep切换到其他协程，保证不在当前协程中循环运行
                gevent.sleep(0)
                continue
        print(self.Remote_Addr, "写业务退出")

    def RunHandlerForWorker(self, req):
        """
        采用任务队列运行Handler，如果未设置worker数量或数量为0时，默认不开启队列运行
        :param req:
        :return:
        """
        if GlobalObject.WorkerPoolSize <= 0:
            return
        # 已经启动工作池机制，将消息交给Worker处理
        self.msgHandler.SendMsgToTaskQueue(req)

    def RunHandler(self, request: IRequest):
        """
        采用协程运行Handler，如果设置了worker数量，默认不开启协程运行。
        :param request:
        :return:
        """
        if GlobalObject.WorkerPoolSize > 0:
            return
        self.msgHandler.DoMsgHandler(request)

    def Stop(self):
        """
        停止链接 结束当前链接的工作
        :return:
        """
        print("链接关闭，ID=", self.ConnID)
        if self.is_closed:
            return
        self.is_closed = True

        # 如果用户注册了该链接的关闭回调业务，那么在此刻应该显示调用
        self.TcpServer.CallOnConnStop(self)
        # 关闭socket链接，回收资源
        self.Conn.close()
        # 将链接从连接管理器中删除
        self.TcpServer.GetConnMgr().Remove(self)

    def GetTCPConnection(self) -> socket.socket:
        """
        获取当前链接绑定的socket conn
        :return:
        """
        return self.Conn

    def GetConnID(self) -> int:
        """
        获取当前链接模块的链接ID
        :return:
        """
        return self.ConnID

    def GetRemoteAddr(self) -> tuple:
        """
        获取远程客户端的TCP状态 IP Port
        :return:
        """
        return self.Remote_Addr

    def SendMsg(self, msgID: int, data: bytes):
        """
        发送数据 将数据发送给远程的客户端
        :param msgID:
        :param data:
        :return:
        """
        if self.is_closed:
            raise Exception("发送数据时客户端链接被关闭")
        try:
            dp = NewDataPack()
            msg = dp.Pack(NewMessage(msgID, len(data), data))
            # self.Conn.send(msg)
            self.msgQueue.put(msg)
        except Exception as e:
            print(e)

    def SetProperty(self, key: str, value: object):
        """
        设置链接属性
        :param key:
        :param value:
        :return:
        """
        self.property[key] = value

    def GetProperty(self, key: str) -> object:
        """
        获取链接属性
        :param key:
        :return:
        """
        if key in self.property.keys():
            return self.property[key]
        return None

    def RemoveProperty(self, key: str):
        """
        移除链接属性
        :param key:
        :return:
        """

        try:
            del self.property[key]
        except:
            return


def NewConnection(tcp_server: IServer, conn: socket.socket, connID: int, remote_addr: tuple, msgHandler: IMsgHandler) -> IConnection:
    c = Connection(tcp_server, conn, connID, remote_addr, msgHandler)
    c.TcpServer.GetConnMgr().Add(c)
    return c
